import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, In, Repository } from 'typeorm';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
import { Video } from 'src/entities/video.entity';
import { VideoHistory } from 'src/entities/video-history.entity';
import { User } from 'src/entities/user.entity';
import { FavoriteVideo } from 'src/entities/favorite-videos.entity';
import * as math from 'mathjs';
import { console } from 'inspector';
import { FollowService } from '../follow/follow.service';

@Injectable()
export class RecommendationService {
  constructor(
    @InjectRepository(Video)
    private videoRepository: Repository<Video>,
    @InjectRepository(VideoHistory)
    private videoHistoryRepository: Repository<VideoHistory>,
    @InjectRepository(User)
    private userRepository: Repository<User>,
    @InjectRepository(FavoriteVideo)
    private favoriteVideoRepository: Repository<FavoriteVideo>,
    @InjectRedis() private readonly redis: Redis,
    private followService: FollowService,
    private dataSource: DataSource,
  ) {}
  /**
   * 为用户推荐视频
   */
  async getRecommendedVideos(
    userId: number,
    limit: number = 10,
  ): Promise<any[]> {
    // 缓存键定义
    const poolCacheKey = `user:${userId}:recommendation_pool`;
    const viewedCacheKey = `user:${userId}:recently_viewed`;

    // 1. 先获取用户最近已查看过的视频ID
    const recentlyViewedIds = await this.redis.lrange(viewedCacheKey, 0, -1);
    const recentlyViewedSet = new Set(
      recentlyViewedIds.map((id) => parseInt(id)),
    );

    // 2. 获取推荐池视频ID列表
    let poolVideoIds = await this.getRecommendationPool(
      userId,
      poolCacheKey,
      Math.max(limit * 5, 30), // 确保推荐池足够大
    );

    // 3. 从池中过滤掉最近已查看的视频
    poolVideoIds = poolVideoIds.filter((id) => !recentlyViewedSet.has(id));

    // 4. 如果过滤后的池太小，重新获取更多推荐并强制刷新推荐池
    if (poolVideoIds.length < limit * 2) {
      // 清除旧的推荐池
      await this.redis.del(poolCacheKey);

      // 获取更大的推荐池
      poolVideoIds = await this.getRecommendationPool(
        userId,
        poolCacheKey,
        Math.max(limit * 10, 100), // 获取更大的推荐池
      );

      // 再次过滤已看过的
      poolVideoIds = poolVideoIds.filter((id) => !recentlyViewedSet.has(id));

      // 如果依然不足，获取一些流行视频但排除已看过的
      if (poolVideoIds.length < limit) {
        const popularVideos = await this.getPopularVideos(limit * 2);
        for (const video of popularVideos) {
          if (
            !recentlyViewedSet.has(video.video_id) &&
            !poolVideoIds.includes(video.video_id)
          ) {
            poolVideoIds.push(video.video_id);
          }
        }
      }
    }

    // 5. 随机选择limit个视频ID (增加多样性)
    let selectedIds: number[] = [];
    if (poolVideoIds.length <= limit) {
      selectedIds = poolVideoIds;
    } else {
      // 改进的洗牌算法，确保更好的随机性
      const shuffled = [...poolVideoIds];
      for (let i = shuffled.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
      }
      selectedIds = shuffled.slice(0, limit);
    }

    // 6. 记录本次推荐的视频ID到最近查看列表
    if (selectedIds.length > 0) {
      // 添加到最近查看列表前端
      await this.redis.lpush(viewedCacheKey, ...selectedIds);
      // 保持最近查看列表在合理大小
      await this.redis.ltrim(viewedCacheKey, 0, 199); // 提高到保留最近200条
      // 设置过期时间 (14天)
      await this.redis.expire(viewedCacheKey, 60 * 60 * 24 * 14);
    }

    // 7. 获取完整视频信息并返回
    return await this.fetchVideosById(selectedIds, userId);
  }

  /**
   * 获取或计算用户的推荐视频池
   */
  private async getRecommendationPool(
    userId: number,
    cacheKey: string,
    poolSize: number = 30,
  ): Promise<number[]> {
    // 1. 尝试从缓存获取推荐池
    const cachedPool = await this.redis.get(cacheKey);

    if (cachedPool) {
      let pool = JSON.parse(cachedPool);
      console.log('缓存推荐池:', pool, pool.length, poolSize);
      if (pool.length >= poolSize) {
        return JSON.parse(cachedPool);
      }
    }

    // 2. 如果缓存没有，计算推荐池
    // 获取用户行为数据
    const userPreferences = await this.getUserPreferences();

    // 如果是新用户或数据不足，返回热门视频作为推荐池
    if (!userPreferences[userId] || Object.keys(userPreferences).length < 2) {
      const popularVideos = await this.getPopularVideos(poolSize);
      const popularIds = popularVideos.map((video) => video.video_id);

      // 缓存这个推荐池 (24小时)
      await this.redis.set(
        cacheKey,
        JSON.stringify(popularIds),
        'EX',
        60 * 60 * 24,
      );

      return popularIds;
    }

    // 3. 计算相似用户
    const similarUsers = this.findSimilarUsers(userId, userPreferences);

    // 4. 基于相似用户构建推荐池
    const recommendedVideos = await this.buildRecommendationPool(
      userId,
      similarUsers,
      poolSize,
    );

    // 5. 缓存推荐池 (缩短为2小时，提高更新频率)
    const recommendationIds = recommendedVideos.map((video) => video.video_id);
    await this.redis.set(
      cacheKey,
      JSON.stringify(recommendationIds),
      'EX',
      60 * 60 * 2, // 从6小时改为2小时
    );

    return recommendationIds;
  }

  /**
   * 构建推荐池
   */
  private async buildRecommendationPool(
    userId: number,
    similarUsers: Array<{ id: number; similarity: number }>,
    poolSize: number,
  ): Promise<any[]> {
    // 获取用户已经看过/互动过的视频ID
    const userInteractedVideos = await this.getUserInteractedVideos(userId);

    // 获取相似用户喜欢但当前用户未看过的视频
    const recommendedVideoIds = new Set<number>();
    const videoScores: Record<number, number> = {};

    // 从最相似的用户开始，最多考虑10个相似用户
    const topSimilarUsers = similarUsers.slice(0, 10);

    for (const { id: similarUserId, similarity } of topSimilarUsers) {
      // 获取相似用户互动过的视频
      const similarUserVideos =
        await this.getUserInteractedVideos(similarUserId);

      // 找出相似用户喜欢但当前用户未看过的视频
      for (const videoId of similarUserVideos) {
        if (!userInteractedVideos.has(videoId)) {
          recommendedVideoIds.add(videoId);
          // 视频得分 = 用户相似度 * 固定权重
          videoScores[videoId] = (videoScores[videoId] || 0) + similarity;
        }
      }
    }

    // 按得分排序推荐视频ID
    let sortedRecommendations = Array.from(recommendedVideoIds)
      .sort((a, b) => videoScores[b] - videoScores[a])
      .slice(0, poolSize);

    // 如果推荐数量不足，添加热门视频
    if (sortedRecommendations.length < poolSize) {
      const popularVideos = await this.getPopularVideos(
        poolSize - sortedRecommendations.length,
      );

      // 只添加用户未看过的热门视频
      for (const video of popularVideos) {
        if (
          !userInteractedVideos.has(video.video_id) &&
          !sortedRecommendations.includes(video.video_id)
        ) {
          sortedRecommendations.push(video.video_id);
          if (sortedRecommendations.length >= poolSize) break;
        }
      }
    }

    // 获取完整的视频信息
    return await this.fetchVideosById(sortedRecommendations, userId);
  }

  /**
   * 获取热门视频（备选推荐）
   */
  private async getPopularVideos(
    limit: number,
    currentUserId?: number,
  ): Promise<any[]> {
    // 查询点赞数最多的视频
    const popularVideos = await this.videoRepository.find({
      where: { status: 'approved', is_delete: 0 },
      order: { likes_count: 'DESC' },
      take: limit,
      relations: ['user'],
    });

    // 附加当前用户的点赞状态
    if (currentUserId) {
      const videoIds = popularVideos.map((v) => String(v.video_id));
      const userLikedVideoIds = await this.redis.smembers(
        `user_likes:${currentUserId}`,
      );

      return popularVideos.map((video) => ({
        ...video,
        is_liked: userLikedVideoIds.includes(String(video.video_id)),
      }));
    }

    return popularVideos;
  }

  /**
   * 获取所有用户的偏好数据矩阵
   */
  private async getUserPreferences(): Promise<
    Record<number, Record<number, number>>
  > {
    // 初始化用户偏好矩阵
    const preferences: Record<number, Record<number, number>> = {};

    // 合并多种行为数据：观看(1分)、点赞(3分)、收藏(5分)

    // 1. 收集观看历史数据
    const viewHistories = await this.videoHistoryRepository.find({
      relations: ['user', 'video'],
    });

    for (const history of viewHistories) {
      const userId = history.user.id;
      const videoId = history.video.video_id;

      if (!preferences[userId]) {
        preferences[userId] = {};
      }

      // 每次观看算1分
      preferences[userId][videoId] = (preferences[userId][videoId] || 0) + 1;
    }

    // 2. 收集点赞数据
    // 从Redis获取所有点赞信息
    const userLikeKeys = await this.redis.keys('user_likes:*');

    for (const key of userLikeKeys) {
      const userId = parseInt(key.split(':')[1]);
      const likedVideos = await this.redis.smembers(key);

      if (!preferences[userId]) {
        preferences[userId] = {};
      }

      for (const videoId of likedVideos) {
        // 点赞加3分
        preferences[userId][parseInt(videoId)] =
          (preferences[userId][parseInt(videoId)] || 0) + 3;
      }
    }

    // 3. 收集收藏数据
    const favorites = await this.favoriteVideoRepository.find({
      relations: ['favorite', 'favorite.user', 'video'],
    });

    for (const favorite of favorites) {
      const userId = favorite.favorite.user.id;
      const videoId = favorite.video.video_id;

      if (!preferences[userId]) {
        preferences[userId] = {};
      }

      // 收藏加5分
      preferences[userId][videoId] = (preferences[userId][videoId] || 0) + 5;
    }

    console.log('用户偏好数据:', preferences);

    return preferences;
  }

  /**
   * 计算用户相似度并找出最相似的用户
   */
  private findSimilarUsers(
    userId: number,
    preferences: Record<number, Record<number, number>>,
  ): Array<{ id: number; similarity: number }> {
    const targetUser = preferences[userId];
    const similarities: Array<{ id: number; similarity: number }> = [];

    // 计算与每个用户的相似度
    for (const otherUserId in preferences) {
      if (parseInt(otherUserId) === userId) continue;

      const otherUser = preferences[otherUserId];
      const similarity = this.calculateCosineSimilarity(targetUser, otherUser);

      if (similarity > 0) {
        similarities.push({
          id: parseInt(otherUserId),
          similarity,
        });
      }
    }

    // 按相似度降序排序
    return similarities.sort((a, b) => b.similarity - a.similarity);
  }

  /**
   * 计算余弦相似度
   */
  private calculateCosineSimilarity(
    user1Prefs: Record<number, number>,
    user2Prefs: Record<number, number>,
  ): number {
    // 找出两个用户共同评价过的项目
    const commonItems = Object.keys(user1Prefs).filter((item) =>
      user2Prefs.hasOwnProperty(item),
    );

    if (commonItems.length === 0) return 0;

    // 计算分子（点积）
    const numerator = commonItems.reduce((sum, item) => {
      return sum + user1Prefs[parseInt(item)] * user2Prefs[parseInt(item)];
    }, 0);

    // 计算用户1的模
    const user1Norm = Math.sqrt(
      Object.values(user1Prefs).reduce(
        (sum, rating) => sum + rating * rating,
        0,
      ),
    );

    // 计算用户2的模
    const user2Norm = Math.sqrt(
      Object.values(user2Prefs).reduce(
        (sum, rating) => sum + rating * rating,
        0,
      ),
    );

    // 避免除零错误
    if (user1Norm === 0 || user2Norm === 0) return 0;

    // 计算余弦相似度
    return numerator / (user1Norm * user2Norm);
  }

  /**
   * 基于相似用户获取推荐
   */
  private async getRecommendationsFromSimilarUsers(
    userId: number,
    similarUsers: Array<{ id: number; similarity: number }>,
    limit: number,
  ): Promise<any[]> {
    // 获取用户已经看过/互动过的视频ID
    const userInteractedVideos = await this.getUserInteractedVideos(userId);

    // 获取相似用户喜欢但当前用户未看过的视频
    const recommendedVideoIds = new Set<number>();
    const videoScores: Record<number, number> = {};

    // 从最相似的用户开始，最多考虑10个相似用户
    const topSimilarUsers = similarUsers.slice(0, 10);

    for (const { id: similarUserId, similarity } of topSimilarUsers) {
      // 获取相似用户互动过的视频
      const similarUserVideos =
        await this.getUserInteractedVideos(similarUserId);

      // 找出相似用户喜欢但当前用户未看过的视频
      for (const videoId of similarUserVideos) {
        if (!userInteractedVideos.has(videoId)) {
          recommendedVideoIds.add(videoId);
          // 视频得分 = 用户相似度 * 固定权重
          videoScores[videoId] = (videoScores[videoId] || 0) + similarity;
        }
      }
    }

    // 按得分排序推荐视频ID
    const sortedRecommendations = Array.from(recommendedVideoIds)
      .sort((a, b) => videoScores[b] - videoScores[a])
      .slice(0, limit);

    // 如果推荐数量不足，添加热门视频
    if (sortedRecommendations.length < limit) {
      const popularVideos = await this.getPopularVideos(
        limit - sortedRecommendations.length,
      );

      // 只添加用户未看过的热门视频
      for (const video of popularVideos) {
        if (
          !userInteractedVideos.has(video.video_id) &&
          !sortedRecommendations.includes(video.video_id)
        ) {
          sortedRecommendations.push(video.video_id);
          if (sortedRecommendations.length >= limit) break;
        }
      }
    }

    // 获取完整的视频信息
    return await this.fetchVideosById(sortedRecommendations, userId);
  }

  /**
   * 获取用户所有互动过的视频ID
   */
  private async getUserInteractedVideos(userId: number): Promise<Set<number>> {
    const interactedVideos = new Set<number>();

    // 1. 观看历史
    const histories = await this.videoHistoryRepository.find({
      where: { user: { id: userId } },
      relations: ['video'],
    });

    histories.forEach((history) => {
      interactedVideos.add(history.video.video_id);
    });

    // 2. 点赞记录
    const likedVideos = await this.redis.smembers(`user_likes:${userId}`);
    likedVideos.forEach((videoId) => {
      interactedVideos.add(parseInt(videoId));
    });

    // 3. 收藏记录
    const favorites = await this.favoriteVideoRepository
      .createQueryBuilder('fv')
      .innerJoin('fv.favorite', 'f')
      .innerJoin('f.user', 'u')
      .where('u.id = :userId', { userId })
      .innerJoinAndSelect('fv.video', 'v')
      .getMany();

    favorites.forEach((fav) => {
      interactedVideos.add(fav.video.video_id);
    });

    return interactedVideos;
  }

  /**
   * 通过ID批量获取视频详情并增强视频数据
   */
  private async fetchVideosById(
    videoIds: number[],
    currentUserId?: number,
  ): Promise<any[]> {
    if (videoIds.length === 0) return [];

    // 获取视频基本信息
    const videos = await this.videoRepository.find({
      where: videoIds.map((id) => ({ video_id: id })),
      relations: ['user', 'category'],
    });

    // 转换为字符串ID用于Redis查询
    const videoIdsStr = videos.map((video) => video.video_id.toString());

    // 获取点赞信息
    let userLikedVideoIds: string[] = [];
    if (currentUserId) {
      userLikedVideoIds = await this.redis.smembers(
        `user_likes:${currentUserId}`,
      );
    }

    // 批量获取Redis中的点赞数
    const redisLikeCounts = await this.redis.hmget(
      'video_likes',
      ...videoIdsStr,
    );

    // 获取评论数 - 修复查询
    let commentCountMap = new Map();
    try {
      // 尝试获取评论数据（使用正确的字段名）
      const commentCounts = await this.dataSource
        .getRepository(Comment)
        .createQueryBuilder('comment')
        .select('comment.video_id', 'videoId') // 可能是video_id而不是videoId
        .addSelect('COUNT(comment.id)', 'count')
        .where('comment.video_id IN (:...videoIds)', { videoIds: videoIdsStr })
        .andWhere('comment.deletedAt IS NULL')
        .groupBy('comment.video_id')
        .getRawMany();

      // 构建评论数映射
      commentCounts.forEach((item) => {
        commentCountMap.set(parseInt(item.videoId), parseInt(item.count));
      });
    } catch (error) {
      console.error('获取评论数时出错:', error);
      // 出错时不中断流程，继续处理其他数据
    }

    // 获取收藏数
    let favoriteCountMap = new Map();
    try {
      const favoriteCounts = await this.dataSource
        .createQueryBuilder()
        .select('favorite_video.video_id')
        .addSelect('COUNT(*)', 'count')
        .from(FavoriteVideo, 'favorite_video')
        .leftJoin('favorite_video.favorite', 'favorite')
        .where('favorite_video.video_id IN (:...videoIds)', {
          videoIds: videoIdsStr,
        })
        .andWhere('favorite.is_delete = 0')
        .groupBy('favorite_video.video_id')
        .getRawMany();

      // 构建收藏数映射
      favoriteCounts.forEach((item) => {
        favoriteCountMap.set(parseInt(item.video_id), parseInt(item.count));
      });
    } catch (error) {
      console.error('获取收藏数时出错:', error);
      // 出错时不中断流程
    }

    // 检查用户是否收藏了这些视频
    let userFavoritedVideoIds: number[] = [];
    if (currentUserId) {
      try {
        const userFavorites = await this.dataSource
          .createQueryBuilder()
          .select('favorite_video.video_id')
          .from(FavoriteVideo, 'favorite_video')
          .innerJoin('favorite_video.favorite', 'favorite')
          .where('favorite.user.id = :userId', { userId: currentUserId })
          .andWhere('favorite_video.video_id IN (:...videoIds)', {
            videoIds: videoIdsStr,
          })
          .andWhere('favorite.is_delete = 0')
          .getRawMany();

        userFavoritedVideoIds = userFavorites.map((item) =>
          parseInt(item.video_id),
        );
      } catch (error) {
        console.error('获取用户收藏状态时出错:', error);
      }
    }

    // 获取当前用户关注的用户列表
    let followingUserIds: number[] = [];
    if (currentUserId) {
      try {
        followingUserIds =
          await this.followService.getFollowingIds(currentUserId);
      } catch (error) {
        console.error('获取用户关注状态时出错:', error);
      }
    }

    // 增强视频数据并按照要求的格式返回
    return videos.map((video, index) => {
      const videoId = video.video_id;
      const authorId = video.user.id;
      const likeCount =
        parseInt(redisLikeCounts[index]) || video.likes_count || 0;

      return {
        video_id: videoId,
        title: video.title,
        description: video.description,
        oss_url: video.oss_url,
        cover_url: video.cover_url,
        duration: video.duration,
        created_at: video.created_at,
        likes_count: video.likes_count,
        user: {
          id: authorId,
          username: video.user.username,
          avatar: video.user.avatar,
        },
        category: video.category
          ? {
              id: video.category.id,
              name: video.category.name,
            }
          : null,
        likeCount,
        commentCount: commentCountMap.get(videoId) || 0,
        favoritesCount: favoriteCountMap.get(videoId) || 0,
        isLiked: userLikedVideoIds.includes(videoId.toString()),
        isFavorited: userFavoritedVideoIds.includes(videoId),
        isFollowed: followingUserIds.includes(authorId),
      };
    });
  }

  /**
   * 更新用户推荐池（在用户行为变化后调用）
   */
  async invalidateRecommendationCache(userId: number): Promise<void> {
    const poolCacheKey = `user:${userId}:recommendation_pool`;
    await this.redis.del(poolCacheKey);
  }

  /**
   * 为游客提供推荐视频（热门视频 + 随机视频）
   */
  async getGuestRecommendations(limit: number = 10): Promise<any[]> {
    console.log('游客推荐视频数量:', limit);
    const cacheKey = 'guest:recommendation:videos';

    // 尝试从缓存获取
    const cachedRecommendations = await this.redis.get(cacheKey);
    console.log('游客推荐缓存:', cachedRecommendations);
    if (cachedRecommendations) {
      const parsedData = JSON.parse(cachedRecommendations);
      // 如果缓存中有足够的视频，则随机打乱后返回指定数量
      if (parsedData.length >= limit) {
        // Fisher-Yates 随机打乱
        for (let i = parsedData.length - 1; i > 0; i--) {
          const j = Math.floor(Math.random() * (i + 1));
          [parsedData[i], parsedData[j]] = [parsedData[j], parsedData[i]];
        }
        return parsedData.slice(0, limit);
      }
    }

    // 获取热门视频(占比70%)
    const hotVideosCount = Math.ceil(limit * 0.7);
    const hotVideos = await this.getPopularVideos(hotVideosCount);

    // 获取随机视频(占比30%)
    const randomVideosCount = limit - hotVideos.length;
    const randomVideos = await this.getRandomVideos(
      randomVideosCount,
      hotVideos.map((v) => v.video_id),
    );

    // 合并结果
    const combinedResults = [...hotVideos, ...randomVideos];

    // 缓存结果(30分钟)
    await this.redis.set(
      cacheKey,
      JSON.stringify(combinedResults),
      'EX',
      60 * 30,
    );

    return combinedResults;
  }

  /**
   * 获取随机视频
   */
  private async getRandomVideos(
    limit: number,
    excludeIds: number[] = [],
  ): Promise<any[]> {
    try {
      // 使用随机排序而不是偏移量，确保真正随机
      const randomVideos = await this.videoRepository
        .createQueryBuilder('video')
        .leftJoinAndSelect('video.user', 'user')
        .leftJoinAndSelect('video.category', 'category')
        .where('video.status = :status', { status: 'approved' })
        .andWhere('video.is_delete = 0')
        .orderBy('RAND()') // MySQL的随机排序
        .take(limit * 2) // 获取更多，以便过滤
        .getMany();

      // 过滤掉要排除的视频
      const filteredVideos = randomVideos
        .filter((video) => !excludeIds.includes(video.video_id))
        .slice(0, limit);

      return filteredVideos;
    } catch (error) {
      console.error('获取随机视频时出错:', error);
      return [];
    }
  }
}
